package com.qmai.openapi;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.PropertyNamingStrategy;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alipay.api.internal.util.AlipaySignature;
import com.qmai.openapi.enums.RequestFormat;
import com.qmai.openapi.enums.RequestVersion;
import com.qmai.openapi.enums.SignType;
import com.qmai.util.dto.ExceptionInfo;
import com.qmai.util.dto.Result;
import kong.unirest.ContentType;
import kong.unirest.HeaderNames;
import kong.unirest.HttpResponse;
import kong.unirest.Unirest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * create by WG on 2020/7/24 17:18
 *
 * @author WangGang
 */
public class DefaultQPayClient implements QPayClient {

	private final static Logger logger = LoggerFactory.getLogger(DefaultQPayClient.class);

	public static final SerializeConfig SNAKE_CASE_CONFIG = new SerializeConfig();

	private static final TypeReference<Map<String, String>> TYPE_REFERENCE = new TypeReference<Map<String, String>>() {};

	static {
		SNAKE_CASE_CONFIG.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
	}

	/**
	 * 密钥Id
	 */
	private String appId;

	/**
	 * 接口名称
	 */
	private String method;

	/**
	 * 仅支持JSON
	 */
	private RequestFormat format;

	/**
	 * 请求使用的编码格式，仅支持utf-8
	 */
	private Charset charset;

	/**
	 * 商户生成签名字符串所使用的签名算法类型，目前支持RSA2
	 */
	private SignType signType;

	/**
	 * 发送请求的时间，格式"yyyy-MM-dd HH:mm:ss"
	 */
	@JSONField(format = "yyyy-MM-dd HH:mm:ss")
	private LocalDateTime timestamp;

	/**
	 * 调用的接口版本
	 */
	private RequestVersion version;

	/**
	 * 私钥字符串
	 */
	private String privateKey;

	/**
	 * 商户请求参数的签名串
	 */
	private String sign;

	/**
	 * 请求地址
	 */
	private String url;

	public DefaultQPayClient(String url, String appId, String privateKey) {
		this(url, appId, RequestFormat.JSON, StandardCharsets.UTF_8, SignType.RSA2, LocalDateTime.now(), privateKey, RequestVersion.V1);
	}


	public DefaultQPayClient(String url,String appId, RequestFormat format, Charset charset, SignType signType, LocalDateTime timestamp, String privateKey, RequestVersion version) {
		this.appId = appId;
		this.format = format;
		this.charset = charset;
		this.signType = signType;
		this.timestamp = timestamp;
		this.version = version;
		this.privateKey = privateKey;
		this.url = url;
	}

	@Override
	public <T> Result<T> execute(String method, QmaiRequest request, Class<T> tClass) {
		return this.execute(method, request, tClass, HttpMethod.POST);
	}


	@Override
	public <T> Result<T> execute(String method, QmaiRequest request, Class<T> tClass, HttpMethod httpMethod) {
		try {
			this.method = method;
			String queryJsonString = JSON.toJSONString(this, SNAKE_CASE_CONFIG);
			Map<String, String> params = JSON.parseObject(queryJsonString, TYPE_REFERENCE);
			params.put(ParamsConstant.BIZ_CONTENT, request.getBizContent());
			this.sign = AlipaySignature.rsaSign(AlipaySignature.getSignContent(params), privateKey, charset.name(), signType.name());
			params.put(ParamsConstant.SIGN, this.sign);
			logger.info("DefaultQPayClient[execute] query params:{}: " + JSON.toJSONString(params));
			HttpResponse<String> response;
			if (httpMethod == HttpMethod.GET) {
				response = Unirest.get(url).header(HeaderNames.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()).queryString((Map) params).asString();
			} else {
				params.remove(ParamsConstant.BIZ_CONTENT);
				String paramsBody = JSON.toJSONString(request, SNAKE_CASE_CONFIG);
				System.out.println("url:"+Unirest.post(url).header(HeaderNames.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()).queryString((Map) params).body(paramsBody).getUrl());
				System.out.println("request_body:"+paramsBody);
				response = Unirest.post(url).header(HeaderNames.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()).queryString((Map) params).body(paramsBody).asString();
			}
			if (response.isSuccess()) {
				TypeReference<Result<T>> typeReference = new TypeReference<Result<T>>(tClass) {};
				return JSON.parseObject(response.getBody(), typeReference);
			} else {
				return Result.failureData(new ExceptionInfo(response.getStatus(), response.getStatusText()));
			}
		} catch (Exception e) {
			return Result.failureData(new ExceptionInfo(0, e.getMessage()));
		}
	}

	public String getAppId() {
		return appId;
	}

	public void setAppId(String appId) {
		this.appId = appId;
	}

	public String getMethod() {
		return method;
	}

	public void setMethod(String method) {
		this.method = method;
	}

	public RequestFormat getFormat() {
		return format;
	}

	public void setFormat(RequestFormat format) {
		this.format = format;
	}

	public Charset getCharset() {
		return charset;
	}

	public void setCharset(Charset charset) {
		this.charset = charset;
	}

	public SignType getSignType() {
		return signType;
	}

	public void setSignType(SignType signType) {
		this.signType = signType;
	}

	public LocalDateTime getTimestamp() {
		return timestamp;
	}

	public void setTimestamp(LocalDateTime timestamp) {
		this.timestamp = timestamp;
	}

	public RequestVersion getVersion() {
		return version;
	}

	public void setVersion(RequestVersion version) {
		this.version = version;
	}

	public String getSign() {
		return sign;
	}

	public void setSign(String sign) {
		this.sign = sign;
	}
}
